package cn.itcast.hotel;

import cn.itcast.hotel.pojo.HotelDoc;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.Map;

/**
 * @author oldwine
 * @date 2022/6/18
 * @Version 1.0
 */
public class EsDocQueryDemo {
    private RestHighLevelClient client;

    @Test
    public void bool() throws IOException {
        //TODO: 1.创建请求对象,封装请求参数
        SearchRequest request = new SearchRequest("hotel");
        SearchSourceBuilder source = request.source();

        //创建布尔查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //设置查询条件
        boolQuery.must(QueryBuilders.matchQuery("name","上海外滩"));
        boolQuery.mustNot(QueryBuilders.termQuery("brand","万怡"));
        boolQuery.filter(QueryBuilders.rangeQuery("price").gte(500).lt(1000));
        //设置布尔条件
        source.query(boolQuery);

        //TODO: 2.发送请求,获取响应结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //TODO: 3.解析显响应结果
        printResult(response);

    }

    @Test
    public void range() throws IOException {
        //TODO: 1.创建请求对象参数,封装请求参数
        SearchRequest request = new SearchRequest("hotel");
        SearchSourceBuilder source = request.source();
        source.query(QueryBuilders.rangeQuery("price").gte(1000).lt(1500));
        //TODO: 2.发送请求,获取响应结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //TODO: 3.解析响应结果
        printResult(response);
    }

    /**
     * 词条查询 查询酒店品牌 brand 希尔顿
     */
    @Test
    public void term() throws IOException {
        //TODO: 1.创建请求对象,封装请求参数
        SearchRequest request = new SearchRequest("hotel");
        SearchSourceBuilder source = request.source();
        source.query(QueryBuilders.termQuery("brand","希尔顿"));

        //TODO: 2.发送请求,获取响应结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //TODO: 3.解析响应结果
        printResult(response);
    }

    /**
     * 全文检索
     */
    @Test
    public void match() throws IOException {
        //TODO: 1.创建请求对象,封装请求参数
        SearchRequest request = new SearchRequest("hotel");
        SearchSourceBuilder source = request.source();

        //1.1 设置查询
        source.query(QueryBuilders.matchQuery("name","外滩如家"));

        //TODO: 2.发送请求,获取响应结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //TODO: 3.解析响应结果
        printResult(response);
    }

    /**
     * 排序和分页
     */
    @Test
    public void sortAndPage() throws IOException {
        int pageNum = 4; //第4页
        int pageSize = 3; //每页3条数据

        //TODO: 1.创建请求对象,封装请求参数
        SearchRequest request = new SearchRequest("hotel");
        SearchSourceBuilder source = request.source();
        //设置查询
        source.query(QueryBuilders.matchAllQuery()); //该行代码不写,默认是查询全部

        //设置排序
        source.sort("score", SortOrder.DESC);
        source.sort("price", SortOrder.ASC);

        //设置分页
        source.from((pageNum - 1) * pageSize);
        source.size(pageSize);

        //TODO: 2.发送请求,获取响应结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //TODO: 3.解析响应结果
        printResult(response);
    }

    /**
     * 高亮查询
     *
     * @throws IOException
     */
    @Test
    public void highlight() throws IOException {
        //TODO: 1.创建请求,封装请求参数
        SearchRequest request = new SearchRequest("hotel");
        SearchSourceBuilder source = request.source();
        //1.1 查询设置
        source.query(QueryBuilders.matchQuery("all", "外滩如家"));
        //1.2 高亮设置
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name"); //设置高亮的字段
        highlightBuilder.preTags("<span>"); //设置前置标签
        highlightBuilder.postTags("</span>");//设置后置标签
        highlightBuilder.requireFieldMatch(false);// 不开启搜索条件字段检查
        source.highlighter(highlightBuilder);

        //TODO: 2.发送请求,获取响应结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //TODO: 3.解析响应结果
        printResult(response);
    }

    /**
     * 解析响应结果
     *
     * @param response
     */
    private void printResult(SearchResponse response) {
        //TODO: 3.解析响应结果
        SearchHits searchHits = response.getHits();
        //获取文档总条数
        long total = searchHits.getTotalHits().value;
        System.out.println("查询的文档总条数为:" + total);

        //获取文档source
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            //获取json字符串
            String hotelJson = hit.getSourceAsString();
            //将json字符串封装成HotelDoc对象
            HotelDoc hotelDoc = JSON.parseObject(hotelJson, HotelDoc.class);

            //获取高亮处理结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                //获取文档关于哪个字段的高亮处理结果
                HighlightField highlightField = highlightFields.get("name");
                //将高亮片段拼接成字符串
                Text[] fragments = highlightField.getFragments();
                String highlightName = StringUtils.join(fragments);
                hotelDoc.setName(highlightName);
            }
            System.out.println(hotelDoc);
        }
    }

    /**
     * 创建ES客户端
     */
    @BeforeEach
    public void init() {
        client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://122.112.171.22:9200")
        ));
    }

    /**
     * 关闭ES客户端
     */
    @AfterEach
    public void close() {
        if (client != null) {
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
